#ifndef AVALON_SERVICELOCATORSERVLET_HPP
#define AVALON_SERVICELOCATORSERVLET_HPP
#include "Avalon/IO/ConnectionState.hpp"
#include "Avalon/Base/Initializer.hpp"
#include "Avalon/ServiceLocator/Directory.hpp"
#include "Avalon/ServiceLocator/ServiceLocatorChannel.hpp"
#include "Avalon/ServiceLocator/ServiceLocatorServices.hpp"
#include "Avalon/Services/ServiceProtocolServlet.hpp"

namespace Avalon {

  /*! \class Initializer
      \brief Initializes a ServiceLocatorServlet.
   */
  template<>
  class Initializer<ServiceLocator::ServiceLocatorServlet> {
    public:

      //! Constructs the Initializer.
      /*!
        \param dataStore The data store to connect to.
      */
      Initializer(ServiceLocator::ServiceLocatorDataStore* dataStore);

    private:
      friend class ServiceLocator::ServiceLocatorServlet;
      ServiceLocator::ServiceLocatorDataStore* m_dataStore;
  };

namespace ServiceLocator {

  /*! \class ServiceLocatorServlet
      \brief Handles ServiceLocator service requests.
   */
  class ServiceLocatorServlet :
      public Services::ServiceProtocolServlet<ServiceLocatorServlet,
      ServiceLocatorChannel> {
    public:

      //! Constructs a ServiceLocatorServlet.
      /*!
        \param initializer The servlet's Initializer.
      */
      ServiceLocatorServlet(
        const Initializer<ServiceLocatorServlet>& initializer);

      ~ServiceLocatorServlet();

    private:
      struct ServiceEntryListing;
      struct SubscriberEntry;
      mutable boost::mutex m_mutex;
      boost::scoped_ptr<ServiceLocatorDataStore> m_dataStore;
      IO::ConnectionState m_dataStoreState;
      std::map<std::string, ServiceLocatorChannel*> m_sessions;
      std::map<std::string, ServiceEntryListing> m_serviceListings;
      std::map<int, ServiceEntry> m_serviceEntries;
      std::map<unsigned int, SubscriberEntry> m_subscriptions;
      int m_nextServiceId;

      void DeleteEntry(const DirectoryEntry::Tag& tag,
        const DirectoryEntry::Tag& parent);
      void PublishDirectoryEntryMoved(const DirectoryEntry::Tag& tag,
        const DirectoryEntry::Tag& parent, bool added);
      bool HasPermission(unsigned int account, unsigned int directory,
        Directory::Permission permission);
      void HandleClose();
      void OnDataStoreClosed();
      void OnLoginRequest(const Services::RequestToken<LoginService>& request,
        const std::string& username, const std::string& password);
      void OnRegisterRequest(
        const Services::RequestToken<RegisterService>& request,
        const std::string& name, const std::string& details);
      void OnUnregisterRequest(
        const Services::RequestToken<UnregisterService>& request,
        int serviceId);
      void OnLocateRequest(const Services::RequestToken<LocateService>& request,
        const std::string& name);
      void OnSubscribeRequest(
        const Services::RequestToken<SubscribeAvailabilityService>& request,
        const std::string& serviceName);
      void OnUnsubscribeRequest(
        const Services::RequestToken<UnsubscribeAvailabilityService>& request,
        const std::string& serviceName);
      void OnLoadDirectoryEntryRequest(
        const Services::RequestToken<LoadDirectoryEntryService>& request,
        unsigned int id);
      void OnLoadParentDirectoryRequest(
        const Services::RequestToken<LoadParentDirectoryService>& request,
        unsigned int id);
      void OnCreateAccountRequest(
        const Services::RequestToken<CreateAccountService>& request,
        const std::string& name, const DirectoryEntry::Tag& directory,
        const std::string& password);
      void OnCreateDirectoryRequest(
        const Services::RequestToken<CreateDirectoryService>& request,
        const std::string& name, const DirectoryEntry::Tag& parent);
      void OnMoveEntryRequest(
        const Services::RequestToken<MoveEntryService>& request,
        unsigned int entryId, unsigned int directoryId);
      void OnDeleteEntryRequest(
        const Services::RequestToken<DeleteEntryService>& request,
        unsigned int entryId);
      void OnSetPermissionsRequest(
        const Services::RequestToken<SetPermissionsService>& request,
        unsigned int accountId, unsigned int entryId,
        Directory::Permission permissions);
      void OnSessionAuthenticationRequest(
        const Services::RequestToken<SessionAuthenticationService>& request,
        const std::string& sessionId, unsigned int saltId);
  };
}
}

#endif // AVALON_SERVICELOCATORSERVLET_HPP
